#!/usr/bin/env python
# coding: utf-8

# In[ ]:


# Selenium库是一个自动化测试工具,用于网络爬虫中来应对JavaScript渲染的页面的抓取.
# chromium是开源的,chromium由不同深度的蓝色构成.chrome由蓝红绿黄四种颜色组成.
# chromium是开发版. chrome是正式版.
# Pyppeteer是基于Python的新特性async实现,它的一些执行支持异步操作.
# 安装: pip install pyppeteer

# pyppeteer详细用法: https://miyakogi.github.io/pyppeteer/reference.html


# In[2]:


import pyppeteer

# 测试基本的页面渲染操作,选用网址: http://quotes.toscrape.com/js/
# 这个页面由JavaScript渲染而成,用requests库请求得到的HTML结果里面不包含页面中所见的条目内容的.
# requests无法完成正常抓取,测试代码如下:
import requests
from pyquery import PyQuery as pq 

url = 'http://quotes.toscrape.com/js/'
response = requests.get(url)
doc = pq(response.text)
print('Quotes:',doc('.quotes').length)
"""
说明: 使用requests请求网页内容,使用pyquery解析页面中的每一个条目. 
观察源码后,发现每个条目中的class名为quote,选用.quote这个CSS选择器来选择,最终输出条目数量.

输出结果为0. 

这个页面是由JavaScript渲染而成的,所看到的内容都是网页加载后又执行了JavaScript之后才呈现出来的.
因此这些条目数据不存在于原始HTML代码中,而requests仅抓取的是原始html代码.

遇到这种类型的网站解决办法有以下三种:
1. 分析网页源代码数据,数据被隐藏在HTML中,以JavaScript变量的形式存在,直接提取.
2. 分析Ajax,很多数据是经过Ajax请求时获取的.可以分析其接口.
3. 模拟JavaScript渲染过程,直接抓取渲染后结果.  Pyppeteer和Selenium用的是第三种方法.
"""


# In[8]:


import asyncio
from pyppeteer import launch 
from pyquery import PyQuery as pq 

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('http://quotes.toscrape.com/js/')
    doc = pq(await page.content())
    print('Quotes:',doc('.quote').length)
    await browser.close()
    
asyncio.get_event_loop().run_until_complete(main())
"""
成功匹配出来class为quote的条目, 总数为10条.
Pyppeteer整个流程完成了浏览器开启,新建页面,页面加载等操作. 
Pyppeteer里面进行了异步操作,需要配合async/await关键词来实现.

首先,launch方法会新建一个Browser对象,然后赋值给browser,
然后调用newPage方法相当于浏览器中新建了一个选项卡,同时新建了一个Page对象。
然后Page对象调用了goto方法就相当于在浏览器中输入了这个URL,浏览器跳转到了对应的页面进行加载,
加载完成之后再调用content方法,返回当前浏览器页面的源代码。然后进一步地,
我们用pyquery进行同样地解析,就可以得到JavaScript渲染的结果了.

"""


# In[11]:


# 模拟网页截图,保存PDF,执行自定义的JavaScript获得特定的内容
import asyncio
from pyppeteer import launch 

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('http://quotes.toscrape.com/js/')
#     await page.screenshot(page='quotesscrape.png')
    await page.pdf(path='quotesscrape.pdf')
    dimensions = await page.evaluate('''() => {
        return {
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight,
            deviceScaleFactor: window.devicePixelRatio,
        }
    }''')
    
    print(dimensions)
    await browser.close()
    
asyncio.get_event_loop().run_until_complete(main())
"""
网页截图保存,网页导出pdf保存,执行 JavaScript 并返回对应数据。
  screenshot 方法可以传入保存的图片路径，另外还可以指定保存格式 type、
 清晰度 quality、是否全屏 fullPage、裁切 clip 等各个参数实现截图。
 
 其内容也是 JavaScript 渲染后的内容，
 另外这个方法还可以指定放缩大小 scale、页码范围 pageRanges、
 宽高 width 和 height、方向 landscape 等等参数
 
 最后我们又调用了 evaluate 方法执行了一些 JavaScript，
 JavaScript 传入的是一个函数，
 使用 return 方法返回了网页的宽高、像素大小比率三个值，
 最后得到的是一个 JSON 格式的对象.
"""


# In[12]:



# 关闭headless模式.
import asyncio 
from pyppeteer import launch 

async def main():
    await launch(headless=False)
    await asyncio.sleep(100)
    
asyncio.get_event_loop().run_until_complete(main())
# 出现一个空白的Chromium界面.


# In[13]:


# 开启调试模式,
# 爬虫时需要分析网页结构还有网络请求.
# 将devtools参数设置为True,每开启一个界面,就弹出一个调试窗口.
import asyncio
from pyppeteer import launch 

async def main():
    browser = await launch(devtools=True)
    page = await browser.newPage()
    await page.goto('https://www.baidu.com')
    await asyncio.sleep(100)
    
asyncio.get_event_loop().run_until_complete(main())
# devtools这个参数设置为True,headless就会被关闭.界面会展示出来.


# In[14]:


# 把"Chrome正受到自动测试软件的控制" 的提示去掉.
# 打开淘宝页面
import asyncio
from pyppeteer import launch 

async def main():
    browser = await launch(headless=False,args=['--disable-infobars'])
    page = await browser.newPage()
    await page.goto('https://www.taobao.com')
    await asyncio.sleep(100)
    
asyncio.get_event_loop().run_until_complete(main())
# 出现了显示的bug,整个浏览器窗口比显示的内容窗口要大.


# In[17]:


# 解决显示的bug,需要设置下window-size还有viewport
import asyncio
from pyppeteer import launch 

width,height = 1366,768 

async def main():
    browser = await launch(headless=False,args=['--disable-infobars',f'--window-size={width},{height}'])
    page = await browser.newPage()
    await page.setViewport({'width':width,'height':height})
    await page.goto('https://www.taobao.com')
    await asyncio.sleep(100)

try:
    asyncio.get_event_loop().run_until_complete(main())
except RuntimeError:
    print('已经运行中')


# In[20]:


# 淘宝主要通过window.navigator.webdriver来对webdriver进行检测，
# 所以我们只需要使用JavaScript将它设置为false即可.
import asyncio 
from pyppeteer import launch

width,height = 1366,768 

async def main():
    browser = await launch(headless=False,args=['--disable-infobars',f'--window-size={width},{height}'])
    page = await browser.newPage()
    await page.setViewport({'width':width,'height':height})
    await page.goto('https://login.taobao.com/member/login.jhtml?redirectURL=https://www.taobao.com/')
    await page.evaluate(
        '''() => { Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
    await asyncio.sleep(100)
    
try:
    asyncio.get_event_loop().run_until_complete(main())
except RuntimeError:
    print('已经运行中')
    
"""
后面要加入输入用户名和密码的代码。
成功规避了webdriver的检测,%save用鼠标拖动模拟就可以登录了.

免去淘宝登录方法:
1.设置用户目录: 
  把关键Cookies保存到本地用户目录. 下次登录时,直接读取并保持登录状态.
  用户目录下存的信息包括了浏览器的基本配置信息,Cache,Cookies等各种信息.
  在浏览器启动时读取这些信息,启动时可以恢复一些历史记录和登录状态信息.
  
"""
# browser = await launch(headless=False, userDataDir='./userdata', args=['--disable-infobars'])

